فایلهای اعلانی TypeScript (.d.ts) را برای باز کردن قفل ایمنی نوع و تکمیل خودکار برای هر کتابخانه جاوااسکریپت مسلط شوید. یاد بگیرید که از @types استفاده کنید، تعاریف خود را ایجاد کنید و کد شخص ثالث را مانند یک حرفهای مدیریت کنید.
باز کردن اکوسیستم جاوااسکریپت: نگاهی عمیق به فایلهای اعلانی TypeScript
TypeScript با آوردن تایپ استاتیک به دنیای پویای جاوااسکریپت، انقلابی در توسعه مدرن وب ایجاد کرده است. این ایمنی نوع مزایای باورنکردنی را ارائه می دهد: گرفتن خطاها در زمان کامپایل، فعال کردن تکمیل خودکار قدرتمند ویرایشگر و ایجاد پایگاههای کد بزرگ به طور قابل توجهی قابل نگهداری تر. با این حال، یک چالش بزرگ زمانی به وجود می آید که ما می خواهیم از اکوسیستم وسیع کتابخانه های جاوااسکریپت موجود استفاده کنیم - که بیشتر آنها با TypeScript نوشته نشده اند. چگونه کد TypeScript به شدت تایپ شده ما اشکال، توابع و متغیرهای یک کتابخانه جاوااسکریپت بدون نوع را درک می کند؟
پاسخ در فایلهای اعلانی TypeScript نهفته است. این فایلها، که با پسوند .d.ts خود قابل شناسایی هستند، پل ضروری بین جهانهای TypeScript و JavaScript هستند. آنها به عنوان یک طرح یا یک قرارداد API عمل می کنند و انواع یک کتابخانه شخص ثالث را بدون حاوی هیچ یک از پیاده سازی واقعی آن توصیف می کنند. در این راهنمای جامع، همه چیزهایی را که برای مدیریت مطمئن تعاریف نوع برای هر کتابخانه جاوااسکریپت در پروژههای TypeScript خود نیاز دارید، بررسی خواهیم کرد.
فایلهای اعلانی TypeScript دقیقاً چه هستند؟
تصور کنید پیمانکاری را استخدام کردهاید که فقط به زبان دیگری صحبت میکند. برای کارکردن مؤثر با آنها، به یک مترجم یا مجموعهای از دستورالعملهای دقیق به زبانی که هر دوی شما میفهمید، نیاز دارید. یک فایل اعلانی دقیقاً این هدف را برای کامپایلر TypeScript (پیمانکار) انجام می دهد.
یک فایل .d.ts فقط حاوی اطلاعات نوع است. شامل:
- امضا برای توابع و روشها (انواع پارامتر، انواع برگشتی).
- تعاریف برای متغیرها و انواع آنها.
- اینترفیسها و نام مستعار نوع برای اشیاء پیچیده.
- تعاریف کلاس، از جمله ویژگیها و روشهای آنها.
- ساختارهای فضای نام و ماژول.
به طور حیاتی، این فایلها حاوی هیچ کد اجرایی نیستند. آنها صرفاً برای تجزیه و تحلیل استاتیک هستند. وقتی یک کتابخانه جاوااسکریپت مانند Lodash را به پروژه TypeScript خود وارد میکنید، کامپایلر به دنبال یک فایل اعلانی متناظر میگردد. اگر یکی را پیدا کند، می تواند کد شما را اعتبارسنجی کند، تکمیل خودکار هوشمند را ارائه دهد و اطمینان حاصل کند که از کتابخانه به درستی استفاده می کنید. اگر این کار را نکند، خطایی مانند: Could not find a declaration file for module 'lodash'. ایجاد می کند.
چرا فایلهای اعلانی برای توسعه حرفهای غیرقابل مذاکره هستند
استفاده از کتابخانه های جاوااسکریپت بدون تعاریف نوع مناسب در یک پروژه TypeScript، دلیل استفاده از TypeScript را در وهله اول تضعیف می کند. بیایید یک سناریوی ساده را با استفاده از کتابخانه ابزار محبوب، Lodash، در نظر بگیریم.
دنیای بدون تعاریف نوع
بدون یک فایل اعلانی، TypeScript هیچ تصوری ندارد که lodash چیست یا چه چیزی را در خود دارد. حتی برای اینکه کد کامپایل شود، ممکن است وسوسه شوید که از یک رفع سریع مانند این استفاده کنید:
const _: any = require('lodash');
const users = [{ 'user': 'barney' }, { 'user': 'fred' }];
// Autocomplete? No help here.
// Type checking? No. Is 'username' the correct property?
// The compiler allows this, but it might fail at runtime.
_.find(users, { username: 'fred' });
در این مورد، متغیر _ از نوع any است. این به طور موثر به TypeScript می گوید: "هیچ چیز مربوط به این متغیر را بررسی نکنید." شما تمام مزایا را از دست می دهید: بدون تکمیل خودکار، بدون بررسی نوع در آرگومان ها و بدون اطمینان از نوع برگشتی. این یک بستر مناسب برای خطاهای زمان اجرا است.
دنیای با تعاریف نوع
حالا، بیایید ببینیم وقتی فایل اعلانی لازم را ارائه می دهیم چه اتفاقی می افتد. پس از نصب انواع (که در مرحله بعد به آن خواهیم پرداخت)، تجربه تغییر می کند:
import _ from 'lodash';
interface User {
user: string;
active?: boolean;
}
const users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];
// 1. Editor provides autocompletion for 'find' and other lodash functions.
// 2. Hovering over 'find' shows its full signature and documentation.
// 3. TypeScript sees that `users` is an array of `User` objects.
// 4. TypeScript knows the predicate for `find` on `User[]` should involve `user` or `active`.
// CORRECT: TypeScript is happy.
const fred = _.find(users, { user: 'fred' });
// ERROR: TypeScript catches the mistake!
// Property 'username' does not exist on type 'User'.
const betty = _.find(users, { username: 'betty' });
تفاوت شب و روز است. ما ایمنی نوع کامل، تجربه توسعه دهنده برتر از طریق ابزارها و کاهش چشمگیر در باگهای بالقوه را به دست می آوریم. این استاندارد حرفه ای برای کار با TypeScript است.
سلسله مراتب یافتن تعاریف نوع
بنابراین، چگونه این فایلهای جادویی .d.ts را برای کتابخانههای مورد علاقه خود دریافت می کنید؟ یک فرآیند تثبیت شده وجود دارد که اکثریت قریب به اتفاق سناریوها را پوشش می دهد.
مرحله 1: بررسی کنید که آیا کتابخانه انواع خود را بستهبندی می کند یا خیر
بهترین حالت این است که یک کتابخانه در TypeScript نوشته شده باشد یا نگهدارندگان آن فایلهای اعلانی رسمی را در همان بسته ارائه دهند. این برای پروژههای مدرن و دارای نگهداری خوب، بهطور فزایندهای رایج میشود.
نحوه بررسی:
- کتابخانه را طبق معمول نصب کنید:
npm install axios - به داخل پوشه کتابخانه در
node_modules/axiosنگاهی بیندازید. آیا هیچ فایل.d.tsمی بینید؟ - فایل
package.jsonکتابخانه را برای یک فیلد"types"یا"typings"بررسی کنید. این فیلد مستقیماً به فایل اعلانی اصلی اشاره دارد. به عنوان مثال،package.jsonAxios شامل:"types": "index.d.ts".
اگر این شرایط برآورده شوند، کارتان تمام است! TypeScript به طور خودکار این انواع بستهبندیشده را پیدا کرده و از آنها استفاده می کند. هیچ اقدام دیگری لازم نیست.
مرحله 2: پروژه DefinitelyTyped (@types)
برای هزاران کتابخانه جاوااسکریپت که انواع خود را بستهبندی نمیکنند، جامعه جهانی TypeScript یک منبع باورنکردنی ایجاد کرده است: DefinitelyTyped.
DefinitelyTyped یک مخزن متمرکز و تحت مدیریت انجمن در GitHub است که فایلهای اعلانی با کیفیت بالا را برای تعداد زیادی از بستههای جاوااسکریپت میزبانی میکند. این تعاریف در رجیستری npm تحت دامنه @types منتشر می شوند.
نحوه استفاده از آن:
اگر یک کتابخانه مانند lodash انواع خود را بستهبندی نکند، به سادگی بسته @types مربوطه آن را به عنوان یک وابستگی توسعه نصب می کنید:
npm install --save-dev @types/lodash
قرارداد نامگذاری ساده و قابل پیش بینی است: برای بسته ای به نام package-name، انواع آن تقریباً همیشه در @types/package-name خواهند بود. می توانید انواع موجود را در وب سایت npm یا مستقیماً در مخزن DefinitelyTyped جستجو کنید.
چرا --save-dev؟ فایلهای اعلانی فقط در طول توسعه و کامپایل مورد نیاز هستند. آنها حاوی هیچ کد زمان اجرا نیستند، بنابراین نباید در بسته تولید نهایی شما گنجانده شوند. نصب آنها به عنوان devDependency این جداسازی را تضمین می کند.
مرحله 3: وقتی هیچ نوعی وجود ندارد - نوشتن نوع خود
اگر از یک کتابخانه قدیمیتر، خاص یا داخلی خصوصی استفاده می کنید که انواع را بستهبندی نمیکند و در DefinitelyTyped وجود ندارد، چه؟ در این حالت، باید آستینهای خود را بالا بزنید و فایل اعلانی خود را ایجاد کنید. در حالی که ممکن است این کار دلهرهآور به نظر برسد، می توانید ساده شروع کنید و در صورت نیاز جزئیات بیشتری اضافه کنید.
رفع سریع: اعلامیه ماژول محیطی کوتاه
گاهی اوقات، فقط باید پروژه خود را بدون خطا کامپایل کنید تا یک استراتژی تایپ مناسب را بفهمید. می توانید یک فایل در پروژه خود ایجاد کنید (به عنوان مثال، declarations.d.ts یا types/global.d.ts) و یک اعلان کوتاه اضافه کنید:
// in a .d.ts file
declare module 'some-untyped-library';
این به TypeScript می گوید: "به من اعتماد کن، ماژولی به نام 'some-untyped-library' وجود دارد. فقط هر چیزی که از آن وارد می شود را از نوع any در نظر بگیر." این خطای کامپایلر را ساکت می کند، اما همانطور که بحث کردیم، تمام ایمنی نوع را برای آن کتابخانه قربانی می کند. این یک پچ موقت است، نه یک راه حل طولانی مدت.
ایجاد یک فایل اعلامیه سفارشی اساسی
یک رویکرد بهتر این است که تعریف انواع را برای بخشهایی از کتابخانهای که واقعاً استفاده میکنید، شروع کنید. بیایید بگوییم یک کتابخانه ساده به نام `string-utils` داریم که یک تابع واحد را صادر می کند.
// In node_modules/string-utils/index.js
module.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
ما می توانیم یک فایل string-utils.d.ts در یک دایرکتوری اختصاصی `types` در ریشه پروژه خود ایجاد کنیم.
// In my-project/types/string-utils.d.ts
declare module 'string-utils' {
export function capitalize(str: string): string;
// You could add other function definitions here as you use them
// export function slugify(str: string): string;
}
اکنون، باید به TypeScript بگوییم که کجا تعاریف نوع سفارشی ما را پیدا کند. ما این کار را در tsconfig.json انجام می دهیم:
{
"compilerOptions": {
// ... other options
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
}
}
با این تنظیم، وقتی import { capitalize } from 'string-utils'، TypeScript فایل اعلانی سفارشی شما را پیدا می کند و ایمنی نوعی را که تعریف کرده اید ارائه می دهد. با استفاده از ویژگی های بیشتر کتابخانه می توانید به تدریج این فایل را بسازید.
غواصی عمیق تر: نوشتن فایلهای اعلامیه
بیایید برخی از مفاهیم پیشرفتهتر را که هنگام نوشتن یا خواندن فایلهای اعلامیه با آنها مواجه خواهید شد، بررسی کنیم.
اعلام انواع مختلف صادرات
ماژولهای جاوااسکریپت میتوانند موارد را به روشهای مختلف صادر کنند. فایل اعلامیه شما باید با ساختار صادرات کتابخانه مطابقت داشته باشد.
- صادرات نامگذاریشده: این رایجترین است. ما آن را در بالا با `export function capitalize(...)` دیدیم. همچنین می توانید ثابت ها، رابط ها و کلاس ها را صادر کنید.
- صادرات پیشفرض: برای کتابخانههایی که از `export default` استفاده میکنند.
- UMD Globals: برای کتابخانههای قدیمیتری که برای کار در مرورگرها از طریق برچسب
<script>طراحی شدهاند، اغلب خود را به شیء سراسری `window` متصل میکنند. می توانید این متغیرهای سراسری را اعلام کنید. - `export =` and `import = require()`: این نحو برای ماژولهای قدیمیتر CommonJS است که از `module.exports = ...` استفاده میکنند. برای مثال، اگر یک کتابخانه `module.exports = myClass;` را انجام دهد.
declare module 'my-lib' {
export const version: string;
export interface Options { retries: number; }
export function doSomething(options: Options): Promise
declare module 'my-default-lib' {
// For a function default export
export default function myCoolFunction(): void;
// For an object default export
// const myLib = { name: 'lib', version: '1.0' };
// export default myLib;
}
// Declares a global variable '$' of a certain type
declare var $: JQueryStatic;
// in my-class.d.ts
declare class MyClass { constructor(name: string); }
export = MyClass;
// in your app.ts
import MyClass = require('my-class');
const instance = new MyClass('test');
در حالی که با ماژولهای ES مدرن کمتر رایج است، این برای سازگاری با بسیاری از بستههای Node.js قدیمیتر اما هنوز هم به طور گسترده مورد استفاده قرار میگیرد، بسیار مهم است.
تقویت ماژول: گسترش انواع موجود
یکی از قدرتمندترین ویژگی ها تقویت ماژول (همچنین به عنوان ادغام اعلان شناخته می شود) است. این به شما امکان می دهد ویژگی ها را به رابط های موجود تعریف شده در فایل اعلامیه بسته دیگری اضافه کنید. این برای کتابخانههایی با معماری افزونه، مانند Express یا Fastify، بسیار مفید است.
تصور کنید از یک میانافزار در Express استفاده میکنید که یک ویژگی `user` به شیء `Request` اضافه میکند. بدون تقویت، TypeScript شکایت می کند که `user` در `Request` وجود ندارد.
در اینجا نحوه اطلاع رسانی این ویژگی جدید به TypeScript آمده است:
// in your types/express.d.ts file
// We must import the original type to augment it
import { UserProfile } from './auth'; // Assuming you have a UserProfile type
// Tell TypeScript we're augmenting the 'express-serve-static-core' module
declare module 'express-serve-static-core' {
// Target the 'Request' interface inside that module
interface Request {
// Add our custom property
user?: UserProfile;
}
}
اکنون، در سرتاسر برنامه خود، شیء Express `Request` به درستی با ویژگی اختیاری `user` تایپ می شود، و شما ایمنی نوع کامل و تکمیل خودکار را دریافت خواهید کرد.
دستورالعمل های سه اسلش
ممکن است گاهی اوقات نظراتی را در بالای فایلهای .d.ts مشاهده کنید که با سه اسلش (///) شروع میشوند. اینها دستورالعمل های سه اسلش هستند که به عنوان دستورالعمل های کامپایلر عمل می کنند.
/// <reference types="..." />: این رایجترین آنهاست. به طور صریح تعاریف نوع بسته دیگری را به عنوان یک وابستگی شامل می شود. به عنوان مثال، انواع یک افزونه WebdriverIO ممکن است شامل/// <reference types="webdriverio" />باشد زیرا انواع خود آن به انواع اصلی WebdriverIO بستگی دارد./// <reference path="..." />: این برای اعلام وابستگی به فایل دیگری در همان پروژه استفاده می شود. این یک نحو قدیمی تر است که تا حد زیادی با واردات ماژول ES جایگزین شده است.
بهترین شیوه ها برای مدیریت فایلهای اعلامیه
- انواع بستهبندیشده را ترجیح دهید: هنگام انتخاب بین کتابخانهها، از آنهایی که در TypeScript نوشته شدهاند یا تعاریف نوع رسمی خود را بستهبندی میکنند، حمایت کنید. این نشان دهنده تعهد به اکوسیستم TypeScript است.
@typesرا درdevDependenciesنگه دارید: همیشه بستههای@typesرا با--save-devیا-Dنصب کنید. آنها برای کد تولید شما مورد نیاز نیستند.- تراز کردن نسخهها: یک منبع رایج خطا، عدم تطابق بین نسخه کتابخانه و نسخه
@typesآن است. یک نسخه اصلی در یک کتابخانه (به عنوان مثال، از v2 به v3) احتمالاً تغییرات مخربی در API خود خواهد داشت که باید در بسته@typesمنعکس شود. سعی کنید آنها را همگام نگه دارید. - از
tsconfig.jsonبرای کنترل استفاده کنید: گزینه های کامپایلرtypeRootsوtypesدرtsconfig.jsonمی توانند به شما کنترل دقیقی بر جایی که TypeScript به دنبال فایل های اعلامیه می گردد، بدهند.typeRootsبه کامپایلر می گوید کدام پوشه ها را بررسی کند (به طور پیش فرض،./node_modules/@typesاست)، وtypesبه شما امکان می دهد به صراحت لیست کنید که کدام بسته های نوع را شامل کنید. - به عقب کمک کنید: اگر یک فایل اعلامیه جامع برای کتابخانهای مینویسید که ندارد، کمک به پروژه DefinitelyTyped را در نظر بگیرید. این یک راه فوق العاده برای بازپرداخت به جامعه جهانی توسعه دهندگان و کمک به هزاران نفر دیگر است.
نتیجه گیری: قهرمانان ناشناخته ایمنی نوع
فایلهای اعلانی TypeScript قهرمانان ناشناختهای هستند که ادغام یکپارچه دنیای پویا و گسترده جاوااسکریپت را در یک محیط توسعه قوی و ایمن از نظر نوع امکانپذیر میکنند. آنها پیوند حیاتی هستند که به ابزارهای ما قدرت می بخشد، از خطاهای بی شماری جلوگیری می کند و پایگاههای کد ما را مقاوم تر و مستندتر می کند.
با درک نحوه یافتن، استفاده و حتی ایجاد فایل های .d.ts خود، شما فقط یک خطای کامپایلر را برطرف نمی کنید - بلکه کل گردش کار توسعه خود را ارتقا می دهید. شما پتانسیل کامل TypeScript و اکوسیستم غنی کتابخانه های جاوااسکریپت را باز می کنید و یک هم افزایی قدرتمند ایجاد می کنید که منجر به نرم افزارهای بهتر و مطمئن تر برای مخاطبان جهانی می شود.